package org.grow.authfilter.config;



import com.fasterxml.jackson.databind.ObjectMapper;
import org.grow.authfilter.entity.UserInfo;
import org.grow.authfilter.entity.UserLoginDto;
import org.grow.authfilter.service.NormalAccessDecision;
import org.grow.authfilter.service.SwaggerAuth;
import org.grow.authfilter.service.UrlDataSource;
import org.grow.authfilter.service.UserInfoService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import javax.servlet.Filter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author: xwg
 * @CreateDate: 21-7-15
 */

@EnableRedisHttpSession
@EnableWebSecurity
public class SecurityCenter extends WebSecurityConfigurerAdapter {

    //    UserInfo => UserDetails =>Principle => Auth Authentication
//    UserInfoService  => UserDetailsService loadUserByUsername
//    AuthSuccessHandler AuthFailureHandler
//    PasswordEncoder
//    登录 =》 验证身份获取cookie
//    SPA  single page application 装载应用程序
//

//    @Bean
//    public static ConfigureRedisAction configureRedisAction() {
//
//        return ConfigureRedisAction.NO_OP;
//    }

    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;
    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;
    @Autowired
    private UrlDataSource urlDataSource;
    @Autowired
    private NormalAccessDecision normalAccessDecision;
    @Autowired
    private SwaggerAuth swaggerAuth;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().disable();
        http.csrf().disable();
        http.httpBasic().disable();

        http.authorizeRequests()
                .antMatchers("/**")
                .permitAll();
        http.formLogin()
//        UsernamePasswordAuthenticationFilter
                .loginProcessingUrl("/login")
                .usernameParameter("username")
                .passwordParameter("password")
                .successHandler(authenticationSuccessHandler)
                .failureHandler(authenticationFailureHandler);

//        UserInfo userInfo = new UserInfo();
//        userInfo.setUserId(-1);
//        userInfo.setUsername("anonymous007");
//        userInfo.setPassword("666");
//        userInfo.setLastLoginTime(LocalDateTime.now());
//        userInfo.setIsEnable(true);
//        userInfo.setNickname("某匿名用户");
//        http.anonymous().principal(userInfo);
          http.anonymous().principal(swaggerAuth.supplyAuth());
//        http.anonymous().disable();

        http.logout()
                .logoutUrl("/logout")
                .deleteCookies("JSESSIONID")
                .invalidateHttpSession(true)
                .logoutSuccessHandler((request, response, auth) -> {
                    response.setStatus(200);
                    response.getWriter().println("good bye " + auth.getName());
                });

//        FilterSecurityInterceptor

        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
//                        第二步的发挥空间
                        o.setSecurityMetadataSource(urlDataSource);
//                        第三步的发挥空间
                        o.setAccessDecisionManager(normalAccessDecision);

                        return o;
                    }
                }).anyRequest().authenticated();

        // ExceptionTranslateFilter
        http.exceptionHandling()
                .accessDeniedHandler((req, res, e) -> {
                    res.setStatus(930);
                    res.getWriter().println(e.getMessage());
                });
//                .authenticationEntryPoint((req, res, e) ->{
//                    res.setStatus(990);
//                    res.getWriter().println(e.getMessage());
//                });

    }

    @Autowired
    private UserInfoService userInfoService;

    //第二步的要求
    @Override
    protected UserDetailsService userDetailsService() {
        return userInfoService;
    }



//    @Bean
    public FilterRegistrationBean<Filter> corsBean(){
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.setMaxAge(3600l);
        corsConfiguration.addAllowedOriginPattern("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.setAllowedMethods
                (Stream.of("POST","GET","DELETE","PUT","OPTIONS")
                        .collect(Collectors.toList()));

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**",corsConfiguration);
        CorsFilter corsFilter = new CorsFilter(source);
        FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
        bean.setFilter(corsFilter);
        bean.setOrder(-1000);
        return bean;
    }
    @Autowired
    private ObjectMapper objectMapper;
    @Bean
    @ConditionalOnMissingBean(AuthenticationSuccessHandler.class)
    public AuthenticationSuccessHandler authenticationSuccessHandler(){
        return new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                UserInfo principal = (UserInfo) authentication.getPrincipal();

                response.setStatus(200);
                UserLoginDto userLoginDto = new UserLoginDto();

                BeanUtils.copyProperties(principal,userLoginDto);
                String s = objectMapper.writeValueAsString(userLoginDto);
                response.getWriter().println(s);
            }
        };
    }
    @Bean
    @ConditionalOnMissingBean(AuthenticationFailureHandler.class)
    public AuthenticationFailureHandler authenticationFailureHandler(){
        return new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest request,
                                                HttpServletResponse response,
                                                AuthenticationException e) throws IOException, ServletException {
                System.out.println("登录失败异常类型 "+e.getClass());
                if(e instanceof BadCredentialsException){
//                    DaoAuthenticationProvider
                    response.setStatus(960);
                    response.getWriter().println(e.getMessage());
                }
                //DisabledException
                else if (e instanceof DisabledException){
                    response.setStatus(961);
                    response.getWriter().println(e.getMessage());
                }
                else {
                    response.setStatus(930);
                    response.getWriter().println(e.getMessage());

                }
            }
        };
    }


}
